﻿using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using Plugins.UserControls;
using StockModel;
using Utils;
using Utils.Wpf;
using System.Threading;

namespace Plugins.Base.Historical
{
	public abstract class ControlPanelVMBase<T> : ViewModelBase where T : HistoricalSettingsBase, ICopyable<T>, ICloneable<T>
	{
		#region Attributes

		private readonly HistoricalEngineBase _engine;
		private readonly BackgroundWorker _pluginExecutor;
		
		private string _progressText;

		#endregion

		#region Properties

		public ICommand RunStrategyCommand { get; private set; }

		public PresetSelectorVM<T> PresetSelectorVM { get; private set; }
		public StockSelectorVM StockSelectorVM { get; private set; }
		public DateSelectorVM DateSelectorVM { get; private set; }
		public HourSelectorVM HourSelectorVM { get; private set; }
		public StockParametersSelectorVM StockParametersSelectorVM { get; private set; }

		public string ProgressText
		{
			get
			{
				return _progressText;
			}
			set
			{
				if (_progressText != value)
				{
					_progressText = value;
					base.OnPropertyChanged("ProgressText");
				}
			}
		}

		public int TotalDaysToExecute
		{
			get
			{
				return _engine.TotalDaysToExecute;
			}
		}

		public int ExecutedDays
		{
			get
			{
				return _engine.ExecutedDays;
			}
		}

		#endregion

		#region Public Methods

		protected ControlPanelVMBase(RawStockDataSource rawStockDataSource, HistoricalEngineBase engine)
		{
			_engine = engine;
			_engine.PropertyChanged += this.Engine_PropertyChanged;

			this.RunStrategyCommand = new DelegateCommand(this.ExecuteRunStrategy, this.CanRunStrategy);

			this.StockSelectorVM = new StockSelectorVM(rawStockDataSource);
			this.DateSelectorVM = new DateSelectorVM(rawStockDataSource);
			this.HourSelectorVM = new HourSelectorVM();
			this.StockParametersSelectorVM = new StockParametersSelectorVM();

			this.StockSelectorVM.PropertyChanged += StockSelectorVM_PropertyChanged; // Local object

			_pluginExecutor = new BackgroundWorker();
			_pluginExecutor.DoWork += PluginExecutor_DoWork;
			_pluginExecutor.RunWorkerCompleted += PluginExecutor_RunWorkerCompleted;
		}

		protected void CreatePresetSelector(T settings)
		{
			this.PresetSelectorVM = new PresetSelectorVM<T>(Assembly.GetCallingAssembly().GetName().Name + Properties.Settings.Default.HistoricalPresetsFilename, settings);
			this.PresetSelectorVM.AfterLoad += PresetSelectorVM_AfterLoad;  // Local object
		}

		protected virtual bool CanRunStrategy()
		{
			return !this.DateSelectorVM.HasValidationErrors && !this.HourSelectorVM.HasValidationErrors && this.StockSelectorVM.IsStockSelected && !this.StockParametersSelectorVM.HasValidationErrors;
		}

		#endregion

		#region Event Handlers

		private void StockSelectorVM_PropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			if (e.PropertyName == "IsStockSelected" && this.StockSelectorVM.IsStockSelected)
			{
				this.DateSelectorVM.LoadAvailableDays(this.StockSelectorVM.SelectedStockName);
			}
		}

		private void PresetSelectorVM_AfterLoad(object sender, EventArgs eventArgs)
		{
			this.DateSelectorVM.ValidateSelectedDays(this.StockSelectorVM.SelectedStockName); // Check if the dates are still valid
		}

		private void PluginExecutor_DoWork(object sender, DoWorkEventArgs e)
		{
			if (string.IsNullOrEmpty(Thread.CurrentThread.Name))
			{
				Thread.CurrentThread.Name = "Historical Plugin Execution Thread";
			}

			_engine.Execute();
		}

		private void PluginExecutor_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if (e.Error != null)
			{
				ExceptionLogger.LogException("Unexpected error executing a historical strategy", e.Error, false);
				MessageBox.Show("Unexpected error executing the strategy.\nError message: " + e.Error.Message, "Unexpected error", MessageBoxButton.OK, MessageBoxImage.Error);
			}

			base.IsBusy = false;
		}

		private void Engine_PropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			if (e.PropertyName == "ExecutedDays" || e.PropertyName == "TotalDaysToExecute")
			{
				this.ProgressText = string.Format("{0}/{1}", _engine.ExecutedDays, _engine.TotalDaysToExecute);	
				base.OnPropertyChanged(e.PropertyName);
			}
		}

		#endregion

		#region Private Methods

		private void ExecuteRunStrategy()
		{
			base.IsBusy = true;

			_pluginExecutor.RunWorkerAsync();
		}

		#endregion
	}
}
